﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Diagnostics;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Collections;
using System.IO;
using System.Text;
using System.Messaging;

namespace TeamFoundation.EventBus
{
    [WebService(Namespace = "http://schemas.conchango.com/TeamFoundation/2005/06/Services/Notification/Extensibility", Description = "")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Notification : System.Web.Services.WebService
    {
        public Notification()
        {
        }

        [WebMethod]
        [SoapDocumentMethod("http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", RequestNamespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
        public void Notify(string eventXml, string tfsIdentityXml, string SubscriptionInfo)
        {
            try
            {
                ProtectedNotify(eventXml, tfsIdentityXml, SubscriptionInfo);
            }
            catch (Exception x)
            {
                // TFS doesn't like errors.

                Trace.TraceWarning("Failed to pump message to bus: {0}",
                    x.ToString().Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n"));
            }
        }

        private static void ProtectedNotify(string eventXml, string tfsIdentityXml, string SubscriptionInfo)
        {
            byte[] message;

            XmlDocument eventDocument = new XmlDocument();
            eventDocument.LoadXml(eventXml);

            #region Generate the raw xml.
            using (var messageWriter = new MemoryStream())
            {
                var settings = new XmlWriterSettings();
                settings.CloseOutput = false;
                settings.Indent = true;
                settings.IndentChars = "  ";
                settings.NewLineOnAttributes = true;
                settings.OmitXmlDeclaration = true;
                settings.Encoding = Encoding.UTF8;

                using (var writer = XmlWriter.Create(messageWriter, settings))
                {
                    writer.WriteStartElement("event", Utilities.EventNamespace);
                    writer.WriteAttributeString("subid", SubscriptionInfo);

                    writer.WriteStartElement("payload", Utilities.EventNamespace);
                    XElement.Parse(eventXml).WriteTo(writer);
                    writer.WriteEndElement();
                    writer.WriteStartElement("identity", Utilities.EventNamespace);
                    XElement.Parse(tfsIdentityXml).WriteTo(writer);
                    writer.WriteEndElement();

                    writer.WriteEndElement();
                }

                message = messageWriter.ToArray();
            }
            #endregion

            #region Send it to the message queues

            using (var data = new Model.Database())
            {
                foreach (var sub in data.SubscriptionSet)
                {
                    // See if we need to send the message.
                    if (!string.IsNullOrEmpty(sub.Filter))
                    {
                        var expression = XPathExpression.Compile(sub.Filter);
                        var navigator = eventDocument.CreateNavigator();
                        object result = navigator.Evaluate(expression);
                        if (result is bool)
                        {
                            if (!(bool)result)
                                continue;
                        }
                        else if (result is XmlNodeList)
                        {
                            if (((XmlNodeList)result).Count == 0)
                                continue;
                        }
                        else if (result is System.Collections.IEnumerable)
                        {
                            var e = (IEnumerable)result;
                            var res = e.Cast<object>().FirstOrDefault();
                            if (res == null)
                                continue;
                        }
                    }

                    try
                    {
                        SendMessage(message, Utilities.ToServerPath(sub.MessageQueue), sub.Name);
                    }
                    catch (Exception x)
                    {
                        Trace.TraceWarning("Failed to pump message to {0}: {1}", sub.MessageQueue,
                            x.ToString().Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n"));
                    }
                }
            }

            #endregion
        }

        private static void SendMessage(byte[] message, string path, string name)
        {
            MessageQueue messageQueue = null;
            try
            {
                Utilities.CreateMessageQueue(path);
            }
            catch
            {
            }

            using (MessageQueueTransaction t = new MessageQueueTransaction())
            {
                t.Begin();
                try
                {
                    using (messageQueue = new MessageQueue(path, false, true, QueueAccessMode.Send))
                    {
                        messageQueue.Send(message, t);
                    }
                    t.Commit();
                }
                catch
                {
                    t.Abort();
                }
            }
        }

    }

}
